''' Process management module

Watchdog manages processes which forms application core. Watchog is started
by application bootstrap code. Process definitions are passed as dictionary.
Watchdog communicates with processes via control_queue.

{'<PROC_NAME>':'process_def': {'target':<process_fnc>,
                               'args':[<argument_list>]},
               'control_queue':...}


'''

from multiprocessing import Process
import threading
import socket

_BUFFER_SIZE = 1024
_HOST = '127.0.0.1'


def start(port, descriptors):
    _start_controlled_processes(descriptors)
    watchdog_thread = threading.Thread(target=_start_monitor,
                                       args=(port, descriptors,))
    watchdog_thread.start()
    print('WATCHDOG: Running on port {0}.'.format(port))
    return watchdog_thread


def _start_controlled_processes(descriptors):
    print('WATCHDOG: Starting controlled processes.')

    for name, descriptor in descriptors.iteritems():
        proc_starter = descriptor['process_def']['target']
        proc_args = descriptor['process_def']['args']

        descriptor['process'] = Process(target=proc_starter, args=proc_args)
        print('WATCHDOG: Starting process: {0}.'.format(name))
        descriptor['process'].start()

    print('WATCHDOG: All controlled processes started.')


def _start_monitor(port, descriptors):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind((_HOST, port))
    sock.listen(1)

    while True:
        client, address = sock.accept()
        command = client.recv(_BUFFER_SIZE)
        client.close()
        monitor = _handle_command(command, descriptors)
        if not monitor:
            break

    sock.close()
    print('WATCHDOG: Stopped.')


def _handle_command(command, descriptors):
    print('WATCHDOG: \'{0}\' command received.'.format(command))
    if command == 'shutdown':
        _handle_shutdown(command, descriptors)
        return False
    else:
        return True


def _handle_shutdown(command, descriptors):
    for name, descriptor in descriptors.iteritems():
        print('WATCHDOG: Shutdown of {0} begin.'.format(name))

        # TODO: control_queue_X disables normal shuwtdow.
        # Will be rafactored as part of the process handling refactor.
        if 'control_queue_X' in descriptor:
            print('WATCHDOG: Clean shutdown of {0}.'.format(name))
            descriptor['control_queue'].put(_ipc_packet(command))
        else:
            print('WATCHDOG: Forced shutdown of {0}.'.format(name))
            print('WATCHDOG: Orphan processes of {0} could remain.'
                  .format(name))
            descriptor['process'].terminate()

        descriptor['process'].join()
        print('WATCHDOG: {0} stopped.'.format(name))


def _ipc_packet(command):
    return {'source': 'watchdog',
            'command': command}


##### UTILITY FUNCTIONS USED BY WATCHDOG CLIENTS ##############################
def send_command(watchdog_port, command):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((_HOST, watchdog_port))
    s.send(command)
    s.close()
